# -*- coding: utf-8 -*-

import os.path
from copy import copy
import cPickle as pickle
from xml.dom.minidom import Document
import xml.sax.handler

from PyQt4 import Qt, QtGui, QtCore

# Хитрые классы и переменные для сохранения в качестве опций, отображения в FeOptions и проверки уникальности фильма.
# Это штука, очень специфическая для данной задачи. Думаю, мало кому, кроме меня это интересно.

class Column:
    def __init__(self, value, name, localName):
        self.value = value
        self.name = name
        self.localName = localName

class ColumnsList(list):
    names      = ( "name",        "year",     "director")
    localNames = (u"Название",   u"Год",     u"Режиссер")

Columns = ColumnsList()
for i, (name, localName) in enumerate(zip(ColumnsList.names, ColumnsList.localNames)):
    column = Column(i, name, localName)
    Columns.append(column)
    setattr(Columns, name, column)

Columns.Names = ColumnsList.names
Columns.LocalNames = ColumnsList.localNames


# опции пользователя
class Options(object):
    configName = os.path.expanduser("~/.movies")
    uniqueColumns = []

    @classmethod
    def save(cls):
        font = QtGui.QApplication.font()
        with open(Options.configName, "wt") as fs:
    
            dispCols = "".join([col.name for col in Options.uniqueColumns])
            if len(dispCols.strip()) == 0:
                raise Exception("save options error")

            fs.write("display_columns = {0}\n".format("; ".join([col.name for col in Options.uniqueColumns])))
            fs.write("font_family = {0}\n".format(font.family()))
            fs.write("font_size = {0}\n".format(font.pointSize()))
            fs.write("font_weight = {0}\n".format(font.weight()))
            fs.write("font_italic = {0}\n".format(font.italic()))
    
    @classmethod
    def load(cls):
        if not os.path.exists(Options.configName):
            Options.uniqueColumns = copy(Columns)
            Options.save()
    
        with open(Options.configName, "rt") as fs:
            lines = fs.readlines()
    
        equations = {}
        for line in lines:
            eq = [s.strip().lower() for s in line.split('=', 1)]
            equations[eq[0]] = eq[1]
    
        uniqueColumnNames = [s.strip() for s in equations["display_columns"].split(";")]
        family = equations["font_family"]
        size = int(equations["font_size"])
        weight = int(equations["font_weight"])
        italic = equations["font_italic"] == 'true'
    
        Options.uniqueColumns = [col for col in Columns if col.name in uniqueColumnNames]
        font = QtGui.QFont(family, size, weight, italic)
        QtGui.qApp.setFont(font)
            
        dispCols = "".join([col.name for col in Options.uniqueColumns])
        if len(dispCols.strip()) == 0:
            raise Exception("load options error")





class Movie:
    def __init__(self):
        self.name = ""
        self.director = ""
        self.year = 0
        self.actors = []
        self.cover = None
        self.description = ""

    def __cmp__(self, other):
        if other is None:
            return 1

        return self.name == other.name and \
               self.director == other.director and \
               self.year == other.year and \
               self.actors == other.actors and \
               self.cover == other.cover and \
               self.description == other.description

class MoviesCollection:
    def getMovies(self):
        return self.__movies
    movies = property(getMovies)

    def __init__(self):
        self.__movies = []

    def __cmp__(self, other):
        return self.movies == other.movies

    def testMovie(self, movie, movieIndex = -1):
        pass
        for i, existed in enumerate(self.movies):
            if i == movieIndex:
                continue
            equal = True
            curMovie = []
            for i, col in enumerate(Options.uniqueColumns):
                curMovie.append(u"%s: %s" % (col.localName, unicode(getattr(movie, col.name))))
                if getattr(movie, col.name) != getattr(existed, col.name):
                    equal = False
                    break
            message = u"Фильм с такими уникальными идентификаторами (%s) уже существует." % u"; ".join(curMovie)
            assert equal == 0, message
        

    def append(self, movie):
        self.testMovie(movie)
        self.movies.append(movie)

    def update(self, movie, movieIndex):
        self.testMovie(movie, movieIndex)
        self.movies[movieIndex] = movie

    def delete(self, movieIndex):
        self.movies.pop(movieIndex)

    def insert(self, movie, movieIndex):
        self.testMovie(movie)
        self.movies.insert(movieIndex, movie)

    def merge(self, otherCollection):
        for item in otherCollection.movies:
            try:
                self.testMovie(item)
            except (AssertionError), e:
                pass
            else:
                self.append(item)

    def serialize(self, indexes=None):
        if indexes is None:
            indexes = range(len(self.movies))

        doc = Document()

        root = doc.createElement("root")
        doc.appendChild(root)
        root.setAttribute("version", "1.0")

        movies = doc.createElement("movies")
        root.appendChild(movies)
        for i in indexes:
            itemMovie = self.movies[i]
            
            movie = doc.createElement("movie")
            movies.appendChild(movie)

            movie.setAttribute("name", unicode(itemMovie.name))
            movie.setAttribute("year", unicode(itemMovie.year))
            movie.setAttribute("director", unicode(itemMovie.director))

            actors = doc.createElement("actors")
            movie.appendChild(actors)
            for itemActor in itemMovie.actors:
                actor = doc.createElement("actor")
                actors.appendChild(actor)
                actor.setAttribute("name", unicode(itemActor))


            cover = doc.createElement("cover")
            movie.appendChild(cover)

            pickledCover = unicode(pickle.dumps(itemMovie.cover, False))
            coverData = doc.createTextNode(pickledCover)
            cover.appendChild(coverData)

            description = doc.createElement("description")
            movie.appendChild(description)
            descriptionData = doc.createTextNode(unicode(itemMovie.description))
            description.appendChild(descriptionData)

        return doc.toprettyxml(indent = "    ")

def deserialize(xmlData):

    class CollectionHandler(xml.sax.handler.ContentHandler):
        def __init__(self):
            self.collection = MoviesCollection()
            self.movie = None
            self.inCover = False
            self.inDescription = False
            self.buffer = ""

        def startElement(self, name, attrs):
            if name == "root":
                assert attrs["version"] == "1.0"
            elif name == "movie":
                self.movie = Movie()
                self.movie.name = attrs["name"]
                self.movie.year = int(attrs["year"])
                self.movie.director = attrs["director"]
            elif name == "actor":
                self.movie.actors.append(attrs["name"])
            elif name == "cover":
                self.buffer = ""
                self.inCover = True
            elif name == "description":
                self.buffer = ""
                self.inDescription = True


        def characters(self, data):
            if self.inCover:
                self.buffer += data
            elif self.inDescription:
                self.buffer += data

        def endElement(self, name):
            if name == "cover":
                self.movie.cover = pickle.loads(str(self.buffer).strip())
                self.inCover = False
                self.buffer = ""
            elif name == "description":
                self.movie.description = self.buffer.strip()
                self.inDescription = False
                self.buffer = ""
            elif name == "movie":
                self.collection.movies.append(self.movie)
                self.movie = None

    handler = CollectionHandler()
    xml.sax.parseString(xmlData.encode("utf-8"), handler)

    return handler.collection
        
    
# tests
if __name__ == "__main__":
    movie = Movie()
    movie.name = ":)"

    coll = MoviesCollection()
    coll.append(movie)

    xmlData = coll.pickleMovieCollection()
    coll2 = unpickleMovieCollection(xmlData)

    assert coll == coll2
